package cn.cosmosx.base.util;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * 文件类型校验工具
 */
@Slf4j
public final class FileTypeCheckUtil {

    public static boolean checkType(File file) {
        try (FileInputStream in = new FileInputStream(file)) {
            return checkType(in, file.getName());
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static boolean checkType(InputStream in, String fileName) {
        return doCheckRowType(in, fileName, Type.values());
    }

    public static boolean checkRawType(File file, Type targetType) {
        if (file == null) {
            throw new RuntimeException("校验的文件为空");
        }
        try (FileInputStream in = new FileInputStream(file)) {
            return checkRowType(in, file.getName(), targetType);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static boolean checkRowType(InputStream in, String fileName, Type targetType) {
        return doCheckRowType(in, fileName, targetType);
    }

    private static boolean doCheckRowType(InputStream in, String fileName, Type... targetTypes) {
        if (in == null) {
            throw new RuntimeException("校验的文件流为空");
        }
        if (fileName == null) {
            throw new RuntimeException("校验的文件名为空");
        }
        String[] arr = fileName.split("\\.");
        if (arr.length < 2) {
            throw new RuntimeException("校验的文件不是特定格式的文件");
        }
        String fileSuffix = arr[arr.length - 1];
        Optional<Type> op = Arrays.stream(targetTypes).filter(f -> Arrays.stream(f.suffix).anyMatch(s -> s.equalsIgnoreCase(fileSuffix))).findAny();
        if (!op.isPresent()) {
            throw new RuntimeException("无法校验当前文件格式:" + fileSuffix);
        }
        Type type = op.get();
        byte[] buff = new byte[type.hexCode.length() / 2];
        String hex = BytesUtil.readBytesToHex(in, buff);
        boolean matched = type.getHexCode().equals(hex);
        if (!matched) {
            Optional<Type> optional = Arrays.stream(Type.values()).filter(c -> c.getHexCode().equals(hex)).findAny();
            optional.ifPresent(t -> log.error("“{}”文件后缀被篡改, 原文件类型:{}", fileName, t.getSuffix()));
        }
        return matched;
    }


    //文件的字节码头
    @Getter
    public enum Type {
        XLS_DOC_PPT("D0CF11E0A1B11AE1", "xls", "doc", "ppt"),
        XLSX_DOCX_PPTX("504B0304", "xlsx", "docx", "pptx"),
        PDF("255044462D312E", "pdf"),
        LIBRE_OFFICE("504B030414", "odg", "odp", "ods", "odt"),

        ZIP("504B03040A", "zip"),
        RAR("52617221", "rar"),
        GZ("1F8B08", "gz"),

        JPEG("FFD8FF", "jpeg"),
        JPG("FFD8FFE0", "jpg"),
        PNG("89504E47", "png"),
        GIF("47494638", "gif"),
        WEBP("52494646", "webp"),
        BMP("424D", "bmp"),
        BPMN("3C3F786D6C", "bpmn");

        Type(String hexCode, String... suffix) {
            this.hexCode = hexCode;
            this.suffix = suffix;
        }

        private final String hexCode;
        private final String[] suffix;

        public static List<String> allSuffix() {
            List<String> r = new ArrayList<>();
            for (Type type : Type.values()) {
                r.addAll(Arrays.asList(type.getSuffix()));
            }
            return r;
        }

    }
}
